
#include "connectionpool.hpp"

shared_ptr<Mysql> Connectionpool::getmysql()
{
    unique_lock<mutex> lock(_queuemutex);
    //如果队列为空，则一直等待
    while (_queue.empty())
    {
        //等待最大的连接时间，如果还是为空，说明连接失败
        //wait_for的返回值是cv_status::timeout
        if(cv_status::timeout==_cv.wait_for(lock,chrono::milliseconds(_connectionTimeout)))
        {
            //如果还是为空，说明连接失败
            if(_queue.empty()){
                cout<<"获取连接超时，获取连接失败"<<endl;
                return nullptr;
            }
        }
    }


    //如果队列不为空，则进行消费

    /*
	shared_ptr智能指针析构时，会把connection资源直接delete掉，相当于
	调用connection的析构函数，connection就被close掉了。
	这里需要自定义shared_ptr的释放资源的方式，把connection直接归还到queue当中
	*/
    //智能指针，这里的第二个参数：改变了智能指针的析构行为，
            //智能指针析构时改为调用下面的lambda表达式
    shared_ptr<Mysql> sp(_queue.front(),[&](Mysql* pcon){
        //考虑线程安全
        unique_lock<mutex> lock(_queuemutex);
        pcon->refreshalivetime();
         _queue.push(pcon);    
    });

    _queue.pop();
    //如果队列为空，则通知消费生产者生产
    if(_queue.empty())
    {
        _cv.notify_all();
    }
    return sp;
}




Connectionpool::Connectionpool()
{
    if (!loadconfigfile())
    {
        cout << "配置文件加载失败" << endl;
    } // 加载配置项

    // 创建初始数量的连接
    for (int i = 0; i < _initSize; i++)
    {
        Mysql *p = new Mysql();
        p->connect(_ip, _port, _username, _password, _dbname);
        p->refreshalivetime();
        _queue.push(p);
        _connnectCnt++;
    }

    // 启动一个新的线程，作为连接的生产者线程
    thread produce(std::bind(&Connectionpool::produceConnectionTask, this));
    // 线程分离
    produce.detach();

    // 启动一个新的定时线程，扫描超过maxIdleTime时间的空闲连接，进行对于的连接回收
	thread scanner(std::bind(&Connectionpool::scannerConnectionTask, this));
	scanner.detach();
}





// 独立的线程，负责生产新的连接
void Connectionpool::produceConnectionTask()
{
    for (;;)
    {
        // 加锁访问队列
        unique_lock<mutex> lock(_queuemutex);
        while (!_queue.empty())
        {
            _cv.wait(lock); // 队列不空，生产线程进入等待状态
        }

        if (_connnectCnt < _maxSize)
        {

            Mysql *p = new Mysql();
            p->connect(_ip, _port, _username, _password, _dbname);
            p->refreshalivetime();
            _queue.push(p);
            _connnectCnt++;
        }
        _cv.notify_all();   //通知消费者可以消费了
    }
}
// 扫描超过maxIdleTime时间的空闲连接，进行对于的连接回收
void Connectionpool::scannerConnectionTask()
{
    for(;;){
        //线程休眠,每_maxldleTime的时间醒来一次，处理超时的连接
        this_thread::sleep_for(chrono::seconds(_maxldleTime));
        unique_lock<mutex> lock(_queuemutex);
        while (_connnectCnt>_initSize)
        {
            Mysql* p=_queue.front();
            if(p->getalivetime()>=(_maxldleTime*1000)){
                //删除该连接
                _queue.pop();
                _connnectCnt--;
                delete p;
            }
            else
            {
                break; //对头都没有超过，其他的连接肯定都没有超过
                //因为每次最先取的都是队头，用完后就返回队尾
                //所以队头就是最长没有被使用的连接。
            }
        }
        

    }
}




// 获取单例的接口
Connectionpool *Connectionpool::getConnectionpool()
{
    static Connectionpool pool;
    return &pool;
}

bool Connectionpool::loadconfigfile()
{
    FILE *pf = fopen("connectpool.conf", "r");
    if (pf == nullptr)
    {
        cout << "connectpool.conf is not exist!" << endl;
        return false;
    }

    // 一行一行读取内容，分别以=和\n作为分割点
    while (!feof(pf))
    {
        char line[1024] = {0};
        fgets(line, 1024, pf);
        string str = line;
        int idx = str.find('=', 0);
        if (idx == -1) // 无效的配置项
        {
            continue;
        }

        // password=123456\n
        int endidx = str.find('\n', idx);
        string key = str.substr(0, idx);
        string value = str.substr(idx + 1, endidx - idx - 1);

        if (key == "ip")
        {
            _ip = value;
        }
        else if (key == "port")
        {
            _port = atoi(value.c_str());
        }
        else if (key == "username")
        {
            _username = value;
        }
        else if (key == "password")
        {
            _password = value;
        }
        else if (key == "dbname")
        {
            _dbname = value;
        }
        else if (key == "initSize")
        {
            _initSize = atoi(value.c_str());
        }
        else if (key == "maxSize")
        {
            _maxSize = atoi(value.c_str());
        }
        else if (key == "maxldleTime")
        {
            _maxldleTime = atoi(value.c_str());
        }
        else if (key == "connectionTimeout")
        {
            _connectionTimeout = atoi(value.c_str());
        }
    }
    cout << "ip:" << _ip << "   port:" << _port << "   username" << _username << "  password" << _password << "  dbname" << _dbname << "  initSize" << _initSize << "  maxSize" << _maxSize << "  maxldleTime" << _maxldleTime << "  connectionTimeout" << _connectionTimeout << endl;
    return true;
}